/**
 * @param {*} req 
 * @param {*} res 
 * @param {*} next 
 * @param {*} config 
 */
export default function useCookies(req, res, next, config = {}) {
    if (req.cookies) {
        next && next()
        return
    }
    req.cookies = {}
    const getCookie = () => req.getHeader?.('Cookie') || req.headers?.cookie
    parseCookie(req.cookies, getCookie())
    req.cookies.get = (name) => {
        const key = encodeURIComponent(name)
        let rc = req.cookies[key] ?? ''
        if (rc !== '') {
            return rc
        }
        const cookie = getCookie()
        if (!cookie) {
            return ''
        }
        return decodeURIComponent(
            cookie.replace(
                new RegExp("(?:(?:^|.*;)\\s*" +
                    key.replace(/[-.+*]/g, "\\$&") +
                    "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")
        ) || '';
    }
    req.cookies.set = (key, value, options) => {
        if ((value ?? '') === '') {
            return
        }
        req.cookies[
            encodeURIComponent(key)
        ] = value
        const str = joinCookie(key, value, options)
        const cookieArr = res.getHeader?.('Set-Cookie') || []
        cookieArr.push(str)
        res.setHeader('Set-Cookie', cookieArr);
    }

    next && next()
}

function parseCookie(cookies, cookieStr) {
    cookieStr?.split(';').forEach((curr) => {
        const [k, v] = curr.split('=')
        if (k != null && v != null) {
            cookies[k] = v
        }
    }, {})
}

/**
 * @param {*} key 
 * @param {*} value 
 * @param {*} options 
 * @returns 
 */
function joinCookie(key, value, options) {
    const {
        path,
        expires,
        domain,
        httpOnly,
        sameSite,
        secure,
    } = options
    const cookie = [
        encodeURIComponent(key), '=', stringifyValue(value),
        '; expires=' + (expires ? expires.toUTCString() : new Date()),
        '; path=' + (path || '/'),
    ]
    if (domain) {
        cookie.push('; domain=' + domain)
    }
    if (httpOnly) {
        cookie.push('; httpOnly=' + !!httpOnly)
    }
    if (sameSite) {
        cookie.push(
            '; sameSite=' +
            (sameSite === true ? 'strict' : sameSite)
        )
    }
    if (secure) {
        cookie.push('; secure')
    }
    return cookie.join('')
}

/**
 * @param {*} value 
 * @returns 
 */
function stringifyValue(value) {
    if (typeof value === 'object')
        JSON.stringify(value)
    else
        return value
}